Processes Primer
Carlos Diaz
@dfirence
Overview
This primer demonstrates why understanding endpoint system processes is crucial for cybersecurity.
Our aim is to highlight their foundational role in system operations and security controls. You'll learn about systetm processes, threads, and fibers, and their importance in system integrity, memory management, and privilege control. By mastering these concepts, you'll gain essential skills for analyzing process structures, identifying vulnerabilities, and managing access controls, vital for effective endpoint security and incident response. Expect more detailed lectures in separate articles to further deepen your expertise and enhance your ability to defend against security threats with precision and confidence.
We will journey through these 3 sections:
- What is a Process, a Thread, and a Fiber?
- What is Process Genealogy?
- What is the Composition of a Process?
Before we begin
Quick Mental Model
We need to have a mental model of the relationships between a Process, Thread, and Fiber.
FOO.EXE executes 2 Threads & 1 Fiber Per Thread
Quick Definitions
What is a Process?
An instance of a running program, containing its own memory space, resources, and one or more threads of execution. It's isolated from other processes for security and stability.
What is a Thread?
The smallest unit of execution within a process. Threads in the same process share the same memory space and resources, allowing for concurrent operations within the process.
At a minimum, every process has 1 thread - commonly referred to as The Main Program Thread. However, the developer of the program can create many threads using the syntax of the language they are developing in.
Specific to the Windows platform, there are security objects known as Tokens which serve as boundary mechanisms to limit access to privileged actions, we will talk more about Tokens later, but for now, the important concepts to know are:
-
All threads have tokens applied to restrict or enable capabilities that are privileged on the operating system.
-
Each thread token affects the code's ability to run successfully.
-
Not all threads have the same token applied. So therefore, one thread can have general privileges, while another thread can have elevated privileges. This is crucial for investigative research conducted during malware analysis.
Ref - Learn More, MSDN Threads
What is a Fiber?
A lightweight unit of execution that must be manually scheduled by the application. Unlike threads, fibers run in the context of a single thread and share the same thread's stack.
Ref - Learn More, MSDN Fibers
Click To Expand - C# Fibers Code Sample
using System;
using System.Runtime.InteropServices;
class Program
{
// Importing ConvertThreadToFiber from kernel32.dll
[DllImport("kernel32.dll")]
private static extern IntPtr ConvertThreadToFiber(
IntPtr lpParameter);
// Importing CreateFiber from kernel32.dll
[DllImport("kernel32.dll")]
private static extern IntPtr CreateFiber(
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter);
// Importing SwitchToFiber from kernel32.dll
[DllImport("kernel32.dll")]
private static extern void SwitchToFiber(
IntPtr lpFiber);
// Pointer to the main fiber
private static IntPtr mainFiber;
// Pointer to the new fiber
private static IntPtr newFiber;
// The routine executed by the new fiber
private static void FiberRoutine(
IntPtr lpParameter)
{
Console.WriteLine("Hello from fiber!");
// Switch back to the main fiber
SwitchToFiber(mainFiber);
}
static void Main()
{
// Convert the current thread to a fiber
mainFiber = ConvertThreadToFiber(IntPtr.Zero);
if (mainFiber == IntPtr.Zero)
{
Console.WriteLine("Error converting thread "
+ "to fiber.");
return;
}
// Create a new fiber
newFiber = CreateFiber(0,
Marshal.GetFunctionPointerForDelegate(
new Action<IntPtr>(FiberRoutine)),
IntPtr.Zero);
if (newFiber == IntPtr.Zero)
{
Console.WriteLine("Error creating fiber.");
return;
}
Console.WriteLine("Switching to new fiber...");
// Switch to the new fiber
SwitchToFiber(newFiber);
// Code execution returns here after switching
Console.WriteLine("Back to main thread.");
}
}
Process Genealogy (Geno)
Origin - Recursive Structures
In endpoint engineering, there are standard software design patterns where recursive data structures are typically visualized as indented trees. When these recursive structures are used to track applications executing on a computer device, we refer to the recursive structure as the Process Tree.
The process tree is composed of process tuples (a pair of processes). This pair of processes typically contains the Parent Process and Child Process as one layer of the tree and as you can imagine, the larger the tree, then the more process tuples it has composing the tree. From a software architecture perspective this helps us design beautiful and performant software to evaluate nested structures with graph algorithms, and in my experience, the process geno concept has always been taught to me around the Level Ancestor Problem used in graph theory.
Given the applied usage of graph theory, where a structure of nodes (processes), and their edges (geno relationship) are expressed, then we now arrive at what process Genealogy is all about - i.e., the inspired version of ancestry applied to the birth and termination of applications on a computing device.
Process Geno - 4 Crucial Relationships From Ancestor to Child Process
Process Tree
Given the understanding of process geno, then we can establish that a Process Tree is a large and recursive data structure comprised of process relationships.
The concept of Process Tree is universal on all platforms (Linux, MacOS, Windows):
- The programs that launch new programs are considered Parent Processes,
- The newly launched/created programs are considered the Child Processes.
Click To Expand - Real World Windows Process Tree
Process Tuple
Given the above, a process tuple, is a pair of processes - the parent process and its immediate child process.
Process Tuple (Pair) - Parent and Immediate Child
Process Triplet
A process triplet is a group of 3 processes - The grandparent, parent, and immediate child process
Process Triplet - Grand Parent, Parent, Immediate Child
Immediate Child Process
I kept referring to the immediate child process for a special reason. There are niche circumstances where a process may not be visually seen as the direct child of the process that requested its creation. These circumstances are crucial for security engineering use cases to ensure investigation efforts preserve accuracy. Here are 2 important scenarios where this can be observed:
-
Deliberately creating or spawning a process with the DETACHED parameter.
-
The Operating System delegates the process creation through facilities not controlled by the developer.
Process Creation - Detached
On Windows, processes can be created by the parent process as detached by passing the defined value of:
- DETACHED_PROCESS (dword 0x00000008) - This value informs the operating system to create a process detached from its immediate parent.
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
In the image below, you can observe how the DETACHED_PROCESS value was used with win32 api CreateProcessA( ... ) to launch Notepad.exe.
Notice the value of the Parent Attribute does not have a parent process.
- Parent attribute shows a value of: <Non-Existent Process(5964)>
Experiment - DETACHED.EXE
To reproduce the behavior with DETACHED_PROCESS, you can use the C++ code listing below.
Let's quickly outline the key points of the highlighted codelines:
- Line 1: Include the <windows.h> header to use the CreateProcess(...) function
- Line 15: Assign the string Notepad.exe as the name of the process to create
- Line 18: Invoke CreateProcess(...)
- Line 20: Provide the process name from Line 15
- Line 24: Provide the DETACHED_PROCESS parameter to the function
#include <windows.h>
#include <iostream>
int main()
{
STARTUPINFOA si;
PROCESS_INFORMATION pi;
// Initialize memory for the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Command line to be executed
LPCSTR commandLine = "Notepad.exe";
// Create the process
if (!CreateProcessA(
NULL, // No module name (use command line)
const_cast<LPSTR>(commandLine), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DETACHED_PROCESS, // Creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi)) // Pointer to PROCESS_INFORMATION structure
{
std::cerr << "CreateProcessA failed (" << GetLastError() << ").\n";
return 1;
}
// Successfully created the process
std::cout << "Process created successfully.\n";
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
Process Creation - Delegated
On Windows, process creation can be delegated to other areas of the operating system, like WMI - Windows Management Instrumentation. If processes are created through the WMI Win32_Process class, the resultant child process will have as a parent the wmi service - i.e., WmiPrvSE.exe.
WmiPrvSE.exe Under DCOM Launcher
You have to ensure you know the correct WmiPrvSE.exe process we are looking for is the immediate child of the svchost (DCOM Launcher).
In the image below, we can see how delegated process creation through WMI places Notepad.exe as the child of WmiPrvSE.exe.
Experiment - WMIC.EXE Requester
To reproduce the experiment from above, you can use the native process on Windows called WMIC.EXE. Specifically run the following command to create a Notepad.exe instance.
wmic.exe process call create Notepad
The diagram below provides a high level mental model of what happens under the hood when process call create is executed by WMIC.EXE.
Process Composition
Now that we have a good sense of what processes are, how they are commonly rendered visually into a process tree, and we also know how to identify processes by their lineage (genealogy), then in this section we will explore the composition of a process, and in general we will concern ourselves with the structures of the Windows platforms, and offer a quick contrast to the equivalent on the Linux platform.
Learning the low-level structures an operating system uses are the foundations to support Digital Forensics & Incident Response
Linux - Structure TASK_STRUCT
On Linux, the equivalent of the _EPROCESS structure from Windows is the task_struct.
This structure is used by the Linux kernel to represent processes and threads. It contains various fields that store information about the process, such as its state, scheduling information, memory management details, and more.
To see what the TASK_STRUCT structure looks like, you can use the TORVALDS Github Repository.
Windows - Structure _EPROCESS
_EPROCESS is a data structure in the Windows operating system's kernel that represents a process. It contains various fields that provide information about the process, such as its state, priority, memory usage, and other attributes. This structure is essential for the operating system to manage and control processes efficiently.
To see what the _EPROCESS data structure looks like under the hood, you can use the NTDIFF Github Repository.
The image below is taken from the Art of Memory Forensics book illustrating the composition of the _EPROCESS data structure on a Windows platform. As we can see, a process has many elements that are essential for the efficient and safe execution of code in a computer. The table below briefly describes each of those elements illustrated.
_EPROCESS Element | Description |
---|---|
Handle table | Contains handles to various kernel objects the process can interact with. |
Loaded modules | Executable modules (such as .exe, .dll, .ocx files) loaded into the process's address space. |
Object | Represents individual kernel objects within the handle table. Example: A file created has a handle of type File indicating the proccess is currently attached to the file. |
SIDs and Privileges | Security Identifiers (SIDs) and privileges associated with the process. |
Threads | Represents the threads that are part of the process. |
Virtual Address Descriptors (VADs) | Data structures that describe the allocation of virtual address space for the process. |
Process Attributes
Now that we know where to reference the _EPROCESS structure for the composition of Windows processes, let's outline the minimum attributes used by endpoint security practitioners. These terms are commonly used to review security logs from endpoint solutions, identify the core subjects involved in the activity and pivoting into impact assessment for the abuse of privileges.
For Impact Assessments:
The attribute of Process Integrity Value allows a defender to know immediately if the user executing a process has administrative privileges without needing to query active directory.
Table of Common Terms Referenced in Endpoint Security Related to _EPROCESS Structure
Human Friendly Name | _EPROCESS Path | Description |
---|---|---|
Process Creation Time | _EPROCESS.CreateTime | The time when the process was created. |
Process Integrity Value | _EPROCESS.Token.Level | The integrity level of the process, part of the security token. |
Process Primary Token Type | _EPROCESS.Token | The primary security token associated with the process. |
Process Session ID | _EPROCESS.SessionProcessLinks | The session ID that the process belongs to. |
PPUID - Parent Process Unique ID | NOTE: Computed by a Developer | Intended to be the real unique identifier that avoids the symptom of pid reuse and assigns a unique tracking mechanism between process instances. We will see this in action separately. |
PPUID - Process Unique ID | NOTE: Computed by a Developer | see PPUID row. |
PPID - Parent Process ID | _EPROCESS.InheritedFromUniqueProcessId | The process ID of the parent process. |
PID - Process ID | _EPROCESS.UniqueProcessId | The process ID of the child ID. |
Process Name | _EPROCESS.ImageFileName | The name of the executable file for the process. |
Process Path | _EPROCESS.SectionObject | The section object, which typically contains the full path to the executable file. |
Process Arguments | _EPROCESS.Peb.ProcessParameters.CommandLine | The command line arguments used to start the process. |
Process Fingerprint | NOTE: Computed by a Developer using hash functions | This is commonly referred to as the process hash and typically associated by MD5, SHA1, SHA2, SHA512. The hash calculation is based on taking a snapshot of the executable file on disk. |
Process Handles - Basics of Behaviors
When a process executes, it is commonly associated with the activity it is responsible for. For example, creating or deleting a file, initiating a network connection, or injecting a process with malicious code. The activities need to be understood as Process Handles.
On Windows platorms, when a process creates a file, the operating system allocates/grants a handle of type File to the process.
The table below, describes the Windows platform Handle Types and Description.
Handle Type | Description |
---|---|
File | Represents a file handle. |
Directory | Represents a directory handle. |
Event | Represents an event object handle. |
Semaphore | Represents a semaphore object handle. |
Timer | Represents a timer object handle. |
Mutex | Represents a mutex object handle. |
Token | Represents a security token handle. |
Process | Represents a process object handle. |
Thread | Represents a thread object handle. |
Desktop | Represents a desktop object handle. |
Window Station | Represents a window station handle. |
Registry Key | Represents a registry key handle. |
Section | Represents a section object handle. |
Job | Represents a job object handle. |
Keyed Event | Represents a keyed event handle. |
Symbolic Link | Represents a symbolic link handle. |
IoCompletion | Represents an I/O completion port handle. |
TpWorkerFactory | Represents a thread pool worker factory handle. |
In the image below, we use process explorer from Sysinternals to inspect the handles of a Notepad.exe process. Notice the Handles portion at the bottom left and the column named Type.
SIDs - Security Identifers
A Windows Security Identifier (SID) is a unique, immutable value assigned to each user, group, or computer account within the Windows operating system. Created by the Security Account Manager (SAM), SIDs are essential for managing access controls and permissions.
They play a crucial role in cybersecurity by allowing the operating system to accurately identify and authenticate users, control access to resources, and audit activities. By associating each account with a distinct SID, Windows ensures that only authorized entities can access sensitive information and system functions, thereby maintaining the security and integrity of the system and enabling precise tracking and enforcement of security policies.
SIDs on Windows platforms can be identified when they are prefixed with S-* and numeric sequences. Active Directory Domains have unique identifiers assigned to them during their creation. Those identifiers are added to the calculation of a SID to create the unique value.
For example: The highlighted part of the SID below represents the identifier of the Active Directory Domain
S-1-5-21-3500888791-3362856982-4023992329-1111
Experiment - WHOAMI.EXE and SIDs
We can use the WHOAMI.EXE program to explore what SIDs look like when tied to user accounts, try the command below in your own machine.
$> whoami /user
USER INFORMATION
----------------
User Name SID
================ ==============================================
dullsec\jdoe-adm S-1-5-21-3500888791-3362856982-4023992329-1111
The important concept we need to establish now is that SIDs are:
- Unique identifiers for users or user groups.
- They arte immutable values in a Windows platform and Windows Enterprise Active Directory Domain.
- Meaningful to security defenders because they are essential for access control strategies used during incident response.
- Easily identified by prefix and suffix to allow defenders to know key things about a subject. Like knowing the default admin account on a computer device by inspecting the prefix of S-1-5- and a suffix of -500.
The table below, and the column SID VALUE has a question mark (?) indicating the enterprise active directory domain value would be different per organization, but the prefix and suffix will remain constant in many cases for well known user and user group objects.
SID NAME | SID VALUE | DESCRIPTION |
---|---|---|
Administrator | S-1-5-?-500 | The default administrative account with full control over the system. |
Guest | S-1-5-21-?-501 | Account for users needing temporary access with limited privileges. |
Domain Admins | S-1-5-21-?-512 | Group with administrative rights across the domain, able to manage all domain-joined computers. |
Local System | S-1-5-18 | Built-in account representing the computer itself with extensive local privileges. |
Everyone | S-1-1-0 | Group including all users, used to assign permissions applicable to every user. |
SID Composition
On Windows platforms the SIDs are composed by the kernel through the _SID structure as seen below. As we move on to the next section of this primer called Process Tokens, we anticipate that tokens are dense data structures that have SIDs appended to it, this is how a TOKEN will be granted access controls on what the process can perform when it executes.
Composition of Windows SIDs - SID Structure
- _SID
- _SID_IDENTIFIER_AUTHORITY
typedef struct _SID {
UCHAR Revision;
UCHAR SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
ULONG SubAuthority[ANYSIZE_ARRAY];
} SID, *PISID;
typedef struct _SID_IDENTIFIER_AUTHORITY {
UCHAR Value[6];
} SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY;
Process Tokens - akin to Privileges
In this section, we will briefly describe the types of tokens and in a separate lecture cover this topic in depth.
Continuing from the SIDs section and their crucial role for access control mechanisms, we now enter the topic of Process Tokens.
On Windows operating systems, there are several types of tokens, each serving different purposes related to security and process management. Perhaps the most crucial concept we need to establish is that a Process Token directly relates to the privilege management mechanisms. For example to restrict access to a resource by individual user or user groups, the process token plays a critical role.
Let's start looking at the composition of a Process Token by referencing the defined Windows Kernel Structure known as _TOKEN.
Windows Token Structure - Fields Omitted For Brevity
typedef struct _TOKEN {
// Fields representing token properties
TOKEN_SOURCE TokenSource; // Source of the token
LUID TokenId; // Unique identifier for the token
LUID AuthenticationId; // Authentication ID of the token
LUID ParentTokenId; // Identifier of the parent token
LARGE_INTEGER ExpirationTime; // Expiration time of the token
TOKEN_USER TokenUser; // User SID for the token
TOKEN_GROUPS TokenGroups; // Group SIDs for the token
TOKEN_PRIVILEGES TokenPrivileges; // Privileges associated with the token
TOKEN_OWNER TokenOwner; // Owner SID for the token
TOKEN_PRIMARY_GROUP TokenPrimaryGroup; // Primary group SID for the token
TOKEN_DEFAULT_DACL TokenDefaultDacl; // Default DACL for the token
TOKEN_TYPE TokenType; // Type of the token (Primary or Impersonation)
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; // Impersonation level (for impersonation tokens)
TOKEN_STATISTICS TokenStatistics; // Statistics about the token
LUID ModifiedId; // Identifier indicating when the token was last modified
//
// Other internal fields omitted for brevity
//
} TOKEN, *PTOKEN;
In the image below, we reference a friendlier depiction of what a Process Token looks like when using awesome diagnostics tools like process explorer from Sysinternals. Notice how there are references to users, groups, and a special field called Mandatory Label\Medium Mandatory Label. That special field is commonly referred to as Process Integrity and it will be crucial for security investigations.
A deep understanding of process tokens significantly enhances DFIR investigations. The process integrity level assigned to a token reveals the user's privileges, allowing us to assess the impact of those privileges during process execution.
Given the above composition of a process token, lets quickly review the types of tokens. There are at least 5 Token Types known, and these tokens will be crucial to identify key aspects of how a process executes.
Token Type | Description |
---|---|
Primary Tokens | Associated with processes and represent the security context of the user. Used by the OS to determine access rights and privileges. |
Impersonation Tokens | Allow a process to temporarily adopt the security context of another user or process. Used in client-server applications. |
Restricted Tokens | A modified version of a primary token with fewer privileges. Used to create a security context with limited access rights. |
Elevated Tokens | Contain additional privileges compared to standard tokens, allowing processes to perform administrative tasks. Used with UAC. |
Filtered Admin Tokens | Created for administrative users when UAC is enabled. They are standard user tokens with administrative privileges filtered out. |
Token Scenarios - RUNAS.EXE
Specific to the token types, let's cover token types applied to practical scenarios, like using the native program called RUNAS.EXE.
RUNAS /user:Administrator "cmd.exe"
SCENARIO | RESULTANT EFFECT |
---|---|
1. Standard User to Administrator: When a standard user launches RUNAS.EXE to run a program as an administrator, the system will prompt for administrator credentials. | Elevated Token: If the credentials are provided, an Elevated Token is granted to the newly created process, allowing it to perform tasks requiring administrative privileges. |
2. Administrator to Administrator with Elevated Privileges: If an administrator launches RUNAS.EXE to start a process with elevated privileges, the process receives an Elevated Token. | Elevated Token: This token contains additional privileges compared to a standard user token, enabling the process to execute administrative tasks. |
3. Administrator Running without Elevation: If an administrator launches RUNAS.EXE without explicitly requesting elevation (e.g., by not using the /trustlevel parameter or similar), the process might receive a Filtered Admin Token. | Filtered Admin Token: This token has administrative privileges filtered out, running the process with standard user rights to minimize security risks. |
Token Privileges
Process Tokens hold privileges which enable or disable functionality, and these privileges are accessed through code. These privileges are identifiable by their unique naming convention prefixed with Se* - for example SeDebugPrivilege is a privilege to conduct debugging actions on processes.
There are many intricate details we will discuss in a separate lecture, but the immediate focus should be based on the excellent research by the SANS Organization where they describe seven privileges that have impactful security implications in their white-paper known as - The Maleficent 7 Privileges.
Privilege | Description |
---|---|
SeAssignPrimaryTokenPrivilege | Allows a process to replace the primary token of a process, potentially allowing the elevation of privileges. |
SeBackupPrivilege | Allows a user to bypass file security to perform backup operations. This privilege allows access to all files, regardless of permissions. |
SeDebugPrivilege | Allows a user to debug and adjust the memory of processes owned by other accounts. This privilege is often used by developers and system debuggers. |
SeLoadDriverPrivilege | Allows a user to load and unload device drivers. This privilege can be used to introduce potentially malicious drivers into the system. |
SeRestorePrivilege | Allows a user to bypass file security to perform restore operations. This privilege allows restoring any files, regardless of permissions. |
SeTakeOwnershipPrivilege | Allows a user to take ownership of any securable object in the system, enabling them to control access to the object. |
SeTcbPrivilege | Stands for "Trusted Computing Base." This privilege allows the user to act as part of the operating system, enabling extensive control over system. |
Token Modifications With Win32 API
Windows tokens can be modified programmatically, and an immediate focus is to learn what are the concrete user-mode APIs that are used to modify tokens. Specifically the API functions AdjustTokenPrivileges(...) and SetTokenInformation(...) are critical to understand because these functions are used to apply the changes to the token.
Defenders that analyze malware armed with this knowledge will be successful at determining key behaviors and characteristics of the malware's intended design. In the table below we outline the key API functions invoked from userland code that is used by offensive techniques targeting token modifications.
Win32 User Mode - APIs Commonly Used To Modify Token Structures
API Name | Description |
---|---|
AdjustTokenPrivileges | Enables or disables privileges in the specified access token. |
DuplicateTokenEx | Creates a new access token that duplicates an existing token, with optional new security attributes. |
GetTokenInformation | Retrieves a specified type of information about an access token. |
OpenProcessToken | Opens the access token associated with a process. |
OpenThreadToken | Opens the access token associated with a thread. |
SetTokenInformation | Sets various types of information for a specified access token. |
The MITRE ATT&CK Enterprise Matrix has extensive documentation on how adversaries leverage malware to modify tokens using the functions covered in this section.
In contrast to the above table, the tabs below provide the function signatures of each of those APIs for quick reference. It is important to understand these user-mode API functions that have complementary kernel-mode API functions.
Win32 User Mode - API Signatures Commonly Used To Modify Token Structures
- AdjustTokenPrivileges
- OpenProcessToken
- OpenThreadToken
- DuplicateTokenEx
- GetTokenInformation
- SetTokenInformation
BOOL AdjustTokenPrivileges(
[in] HANDLE TokenHandle,
[in] BOOL DisableAllPrivileges,
[in, optional] PTOKEN_PRIVILEGES NewState,
[in] DWORD BufferLength,
[out, optional] PTOKEN_PRIVILEGES PreviousState,
[out, optional] PDWORD ReturnLength
);
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
BOOL OpenThreadToken(
[in] HANDLE ThreadHandle,
[in] DWORD DesiredAccess,
[in] BOOL OpenAsSelf,
[out] PHANDLE TokenHandle
);
BOOL DuplicateTokenEx(
[in] HANDLE hExistingToken,
[in] DWORD dwDesiredAccess,
[in, optional] LPSECURITY_ATTRIBUTES lpTokenAttributes,
[in] SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
[in] TOKEN_TYPE TokenType,
[out] PHANDLE phNewToken
);
BOOL GetTokenInformation(
[in] HANDLE TokenHandle,
[in] TOKEN_INFORMATION_CLASS TokenInformationClass,
[out, optional] LPVOID TokenInformation,
[in] DWORD TokenInformationLength,
[out] PDWORD ReturnLength
);
BOOL SetTokenInformation(
[in] HANDLE TokenHandle,
[in] TOKEN_INFORMATION_CLASS TokenInformationClass,
[in] LPVOID TokenInformation,
[in] DWORD TokenInformationLength
);
Win32 Kernel Mode - API Signatures Commonly Used To Modify Token Structures
- ZwAdjustPrivilegesToken
- ZwOpenProcessToken
- ZwOpenThreadToken
- ZwDuplicateToken
- ZwQueryInformationToken
- ZwSetInformationToken
NTSTATUS ZwAdjustPrivilegesToken(
HANDLE TokenHandle,
BOOLEAN DisableAllPrivileges,
PTOKEN_PRIVILEGES NewState,
ULONG BufferLength,
PTOKEN_PRIVILEGES PreviousState,
PULONG ReturnLength
);
NTSTATUS ZwOpenProcessToken(
HANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
PHANDLE TokenHandle
);
NTSTATUS ZwOpenThreadToken(
HANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
BOOLEAN OpenAsSelf,
PHANDLE TokenHandle
);
NTSTATUS ZwDuplicateToken(
HANDLE ExistingTokenHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
BOOLEAN EffectiveOnly,
TOKEN_TYPE TokenType,
PHANDLE NewTokenHandle
);
NTSTATUS ZwQueryInformationToken(
HANDLE TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
PVOID TokenInformation,
ULONG TokenInformationLength,
PULONG ReturnLength
);
NTSTATUS ZwSetInformationToken(
HANDLE TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
PVOID TokenInformation,
ULONG TokenInformationLength
);
Process Memory - VADS
VADS will be covered on a separate lecture in depth.
Virtual Address Descriptors (VADs)
Virtual Address Descriptors (VADs) are kernel-mode structures used by the Windows operating system to manage and describe the virtual address space allocated to a process.VADS themselves do not store actual data like strings for "Hello World." Instead, VADs are structures that manage and describe the memory regions allocated to a process. They contain metadata about these memory regions, such as:
- The start and end addresses
- The size of memory regions
- The protection attributes (e.g., read, write, execute permissions)
- The type of memory (e.g., private, shared, mapped file).
The actual data, like strings, is stored in the physical memory pages that these VADs reference and manage.
To get a feel for what VADs look like and how they describe memory regions of processes, let's use the Volatility3 tool and specifically the windows.vadinfo plugin.
In the output below, we can see the metadata for the processes listed and pay special attention to the columns - Tag and Protection.
Volatility 3 Framework 2.0.0
Progress: 100.00 PDB scanning finished
PID Process Start End Tag Protection CommitCharge PrivateMemory File
------ ---------------- ------------------ ------------------ ------ --------------- ------------- ------------- ----------
4 System 0xfffff8024d400000 0xfffff8024d402000 VadS PAGE_EXECUTE_WRITECOPY 2 0
228 smss.exe 0x000000001feb0000 0x000000001feb1000 VadS PAGE_READONLY 1 0
332 csrss.exe 0x00000000200d0000 0x00000000200d1000 VadS PAGE_READWRITE 1 0
... omitted for brevity ...
VAD Types
Specific to learning VADs, we focus on the types of Vad Types which are viewable through the Tag column from the windows.vadinfo plugin, this will give us an intuitive understanding of what type of memory a specific range is hosting.
Type of VAD | Description |
---|---|
VadS | The standard VAD type, representing a range of committed memory. Includes information about the virtual address range, memory protection attributes, and backing store if mapped to a file. |
VadSec | Represents a section of memory that is mapped to a file. Links the virtual memory range to a specific file on disk. |
VadShared | Indicates a shared memory section, accessible by multiple processes. Used for inter-process communication (IPC) and shared memory segments. |
VadDevicePhysicalMemory | Represents memory mapped to device physical memory. Used for memory regions that directly map to hardware device memory. |
VadImage | Represents an image section, including executable images and dynamic link libraries (DLLs). Maps executable files into the process’s address space. |
VadRotatePhysicalMemory | Indicates memory that rotates across physical memory regions. Used for specific optimizations or hardware interactions. |
VadLargePageSection | Represents a large page section using large pages (2 MB or 1 GB) instead of standard 4 KB pages. Optimizes memory usage for large contiguous memory regions. |
Memory Protection Types
Now that we know what VADs are like we need to quickly learn the Memory Protection types. I encourage you to think of memory protection types similarly to filesystem permissions. One major difference is that memory is typically not exposed as a filesystem would through explorer.exe in a graphical windows system.
However the concepts remain the same, meaning, "things" in memory are:
-
Stored, restricted, read, written, and deleted just as a filesystem does. Except that the way these actions are conducted are through special methods not commonly exposed to an end user, instead it is reserved by the software developer of the program and the direct handling/management by the operating system of the computer.
-
Accessed programmatically through Win32 API functions like VirtualAlloc, WriteProcessMemory, ReadProcessMemory, etc...
The table below outlines the Memory Protection Types on Windows platforms and a brief description of their protection focus.
Memory Protection Type | Description |
---|---|
PAGE_NOACCESS | No access to the memory region. Any access attempt will result in an access violation. |
PAGE_READONLY | Read-only access to the memory region. Write attempts will result in an access violation. |
PAGE_READWRITE | Read and write access to the memory region. |
PAGE_WRITECOPY | Read and write access, but writes are not directly to the memory. Instead, a private copy is made. |
PAGE_EXECUTE | Execute access only. Read or write attempts will result in an access violation. |
PAGE_EXECUTE_READ | Execute and read access. Write attempts will result in an access violation. |
PAGE_EXECUTE_READWRITE | Execute, read, and write access to the memory region. |
PAGE_EXECUTE_WRITECOPY | Execute, read, and copy-on-write access. Writes result in a private copy being made. |
PAGE_GUARD | Marks the memory region as guarded. Access causes a guard page exception and the guard attribute is removed. |
PAGE_NOCACHE | Disables caching for the memory region, ensuring all reads and writes are done directly to physical memory. |
PAGE_WRITECOMBINE | Enables write-combining, which allows the processor to combine multiple write operations for efficiency. |
Conceptual Model Of VADS With WriteProcessMemory
To finish this section, we will leave a mental model using a sequence diagram where a fake application FOO.EXE will invoked the Win32Api WriteProcessMemory(...) and we follow the path of the execution throughout the operating system's key memory management components.
Conclusion
The journey to becoming a top-tier defender is just beginning! You've taken a major step towards mastering endpoint security by diving into the core of Windows processes. Stay tuned for more in-depth lectures that will expand your expertise.
- Understanding these internals equips you with the skills to investigate threats with precision.
- This foundational knowledge sets you on a path to becoming a formidable cybersecurity expert.
- Keep exploring and harnessing the power of this knowledge to protect your systems and networks from ever-evolving cyber threats
We value your feedback and insights, so please leave your comments or questions. Your input helps us improve and tailor future content to your needs.
Click To Expand: References
References
Community Research
Practical References
Specification References
VADS - Tools and Resources
-
Volatility Framework:
- An open-source memory forensics framework used to extract digital artifacts from volatile memory (RAM) dumps. Volatility includes plugins to analyze VADs and other memory structures.
- Volatility GitHub
-
Windows Internals by Mark Russinovich:
- A comprehensive resource for understanding the inner workings of the Windows operating system, including detailed explanations of memory management and VADs.
- Windows Internals Book